package com.example.musicplayer.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.Scroller;

import com.example.musicplayer.R;

public class SlideLinearLayout extends LinearLayout {
    private static final int MIN_DISTANCE = 5;
    private boolean mEnableLeft;
    private boolean mEnableRight;
    private final Scroller mScroller;
    private int mMaxScrollWidthLeft;
    private int mMaxScrollWidthRight;
    private int mStartTouchX;
    private int mStartTouchY;
    private int mStartScrollX;
    private boolean mInterceptTouchEvent = false;
    private boolean mIgnoreTouchEvent = false;
    private boolean mSlideLeft = false;
    private boolean mSlideRight = false;
    private boolean mCanSlideLeft = true;
    private boolean mCanSlideRight = true;
    private boolean mIgnoreDownEvent = false;
    private boolean mStillDown = false;
    private boolean mHasStartScroll = false;
    private StateListener mStateListener;
    private SlideListener mSlideListener;

    public interface StateListener {
        boolean ignoreDownEvent();
    }

    public interface SlideListener {
        void startAutoScroll(boolean isSlideLeft, boolean isSlideRight);

        void endAutoScroll(boolean isSlideLeft, boolean isSlideRight);
    }

    public SlideLinearLayout(Context context) {
        this(context, null);
    }

    public SlideLinearLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SlideLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public SlideLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        mScroller = new Scroller(getContext());
        //get attrs
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SlideLinearLayout);
        mEnableLeft = typedArray.getBoolean(R.styleable.SlideLinearLayout_enableLeft, false);
        mEnableRight = typedArray.getBoolean(R.styleable.SlideLinearLayout_enableRight, true);
        typedArray.recycle();
    }

    public void setStateListener(StateListener listener) {
        mStateListener = listener;
    }

    public void setSlideListener(SlideListener listener) {
        mSlideListener = listener;
    }

    public boolean isEnableLeft() {
        return mEnableLeft;
    }

    public boolean isEnableRight() {
        return mEnableRight;
    }

    public boolean isSlide() {
        return mSlideLeft || mSlideRight;
    }

    public boolean isSlideLeft() {
        return mSlideLeft;
    }

    public boolean isSlideRight() {
        return mSlideRight;
    }

    public boolean canSlideLeft() {
        return mCanSlideLeft;
    }

    public boolean canSlideRight() {
        return mCanSlideRight;
    }

    public boolean canSlideHorizontal() {
        return mCanSlideLeft || mCanSlideRight;
    }

    public void setEnableLeft(boolean enableLeft) {
        mEnableLeft = enableLeft;
        requestLayout();
    }

    public void setEnableRight(boolean enableRight) {
        mEnableRight = enableRight;
        requestLayout();
    }

    public void setCanSlideHorizontal(boolean canSlide) {
        mCanSlideLeft = canSlide;
        mCanSlideRight = canSlide;
    }

    public void resume() {
        if (isSlide()) {
            if (mSlideListener != null) {
                mSlideListener.startAutoScroll(mSlideLeft, mSlideRight);
            }
            mScroller.startScroll(getScrollX(), 0, -getScrollX(), 0, 500);
        } else {
            scrollTo(0, 0);
        }
        invalidate();
    }

    public void resumeWithNoAnim() {
        scrollTo(0, 0);
        mSlideLeft = false;
        mSlideRight = false;
        invalidate();
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mStartTouchX = (int) ev.getX();
                mStartTouchY = (int) ev.getY();
                mStartScrollX = getScrollX();
                mStillDown = true;
                mInterceptTouchEvent = false;
                mIgnoreTouchEvent = false;
                mIgnoreDownEvent = (mStateListener != null && mStateListener.ignoreDownEvent());
                setClickable(!isSlide() && !mIgnoreDownEvent);
                break;
            case MotionEvent.ACTION_MOVE:
                if (canSlideHorizontal() && !mIgnoreTouchEvent && (Math.abs(mStartTouchX - ev.getX()) >= MIN_DISTANCE
                        || mIgnoreDownEvent)) {
                    if (getParent() != null) {
                        getParent().requestDisallowInterceptTouchEvent(true);
                    }
                    mInterceptTouchEvent = true;
                    setPressed(false);
                }
                //if ignore touch event and deliver down
                if (!mInterceptTouchEvent && Math.abs(mStartTouchY - ev.getY()) >= MIN_DISTANCE) {
                    mIgnoreTouchEvent = true;
                }
                if (!mIgnoreTouchEvent && !mIgnoreDownEvent && mInterceptTouchEvent) {
                    int newScrollX = mStartScrollX + mStartTouchX - (int) ev.getX();
                    newScrollX = Math.max(newScrollX, mEnableLeft ? -mMaxScrollWidthLeft : 0);
                    newScrollX = Math.min(newScrollX, mEnableRight ? mMaxScrollWidthRight : 0);
                    scrollTo(newScrollX, 0);
                }
                break;
            case MotionEvent.ACTION_UP:
                mStillDown = false;
                if (!mIgnoreTouchEvent && !mIgnoreDownEvent && canSlideHorizontal()) {
                    if (mSlideListener != null) {
                        mSlideListener.startAutoScroll(mSlideLeft, mSlideRight);
                    }
                    int endScrollX = mStartScrollX + mStartTouchX - (int) ev.getX();
                    if (mEnableRight && endScrollX >= mMaxScrollWidthRight / 2) {
                        endScrollX = mMaxScrollWidthRight;
                        mSlideRight = true;
                        mSlideLeft = false;
                    } else if (mEnableLeft && endScrollX <= -mMaxScrollWidthLeft / 2) {
                        endScrollX = -mMaxScrollWidthLeft;
                        mSlideLeft = true;
                        mSlideRight = false;
                    } else {
                        endScrollX = 0;
                        mSlideLeft = false;
                        mSlideRight = false;
                    }
                    mScroller.startScroll(getScrollX(), 0, endScrollX - getScrollX(), 0);
                    invalidate();
                } else {
                    mInterceptTouchEvent = true;
                }
                break;
            case MotionEvent.ACTION_CANCEL:
                mStillDown = false;
                if (!mIgnoreDownEvent) {
                    resume();
                }
                break;
        }
        super.dispatchTouchEvent(ev);
        return true;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        super.onInterceptTouchEvent(ev);
        return mInterceptTouchEvent;
    }

    @Override
    public boolean performClick() {
        if (!mInterceptTouchEvent && !mIgnoreTouchEvent && !mIgnoreDownEvent && !isSlide()) {
            return super.performClick();
        } else {
            return false;
        }
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            mHasStartScroll = true;
        } else if (mHasStartScroll) {
            if (isSlide() && getScrollX() == 0) {
                mSlideLeft = false;
                mSlideRight = false;
            }
            if (mSlideListener != null) {
                mSlideListener.endAutoScroll(mSlideLeft, mSlideRight);
            }
            mHasStartScroll = false;
        }
        invalidate();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //measure the middle view
        if ((mEnableLeft || mEnableRight) && getOrientation() == LinearLayout.HORIZONTAL) {
            View child;
            LayoutParams lp;
            int widthSpec;
            int heightSpec;
            //measure the middle view
            if (mEnableLeft) {
                child = getChildAt(1);
            } else {
                child = getChildAt(0);
            }
            if (child != null) {
                lp = (LayoutParams) child.getLayoutParams();
                if (lp == null) {
                    lp = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
                    child.setLayoutParams(lp);
                }
                widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY);
                if (lp.height > 0) {
                    heightSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
                } else if (lp.height == ViewGroup.LayoutParams.MATCH_PARENT) {
                    heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY);
                } else {
                    heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST);
                }
                child.measure(widthSpec, heightSpec);
            }
            //measure right child view
            if (mEnableLeft) {
                child = getChildAt(2);
            } else {
                child = getChildAt(1);
            }
            if (child != null) {
                lp = (LayoutParams) child.getLayoutParams();
                if (lp == null) {
                    lp = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
                    child.setLayoutParams(lp);
                }
                //width
                if (lp.width > 0) {
                    widthSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
                } else if (lp.width == ViewGroup.LayoutParams.MATCH_PARENT) {
                    widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY);
                } else {
                    widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST);
                }
                //height
                if (lp.height > 0) {
                    heightSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
                } else if (lp.height == ViewGroup.LayoutParams.MATCH_PARENT) {
                    heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY);
                } else {
                    heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST);
                }
                child.measure(widthSpec, heightSpec);
            }
        }
        //set background
        if (getBackground() == null) {
            setBackgroundResource(R.drawable.ripple_list_view_default);
            setClickable(true);
            setFocusable(true);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        //if enable left, re-layout
        if (mEnableLeft && getChildCount() > 1) {
            View child = getChildAt(0);
            int offset = 0;
            if (child != null) {
                offset = -child.getWidth();
            }
            for (int i = 0; i < getChildCount(); i++) {
                child = getChildAt(i);
                if (child != null) {
                    child.setLeft(child.getLeft() + offset);
                    child.setRight(child.getRight() + offset);
                }
            }
        }
        if (getOrientation() == LinearLayout.HORIZONTAL) {
            int count = getChildCount();
            if (count >= 2) {
                mMaxScrollWidthLeft = Math.abs(getChildAt(0).getLeft());
                mMaxScrollWidthRight = getChildAt(count - 1).getRight() - getWidth();
            } else {
                mMaxScrollWidthLeft = 0;
                mMaxScrollWidthRight = 0;
            }
        } else {
            mMaxScrollWidthLeft = 0;
            mMaxScrollWidthRight = 0;
        }
    }
}